package smallChanges.lineNumberMapping;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import smallChanges.statements.ClassDeclaration;
import smallChanges.statements.ExtractStatements;
import smallChanges.statements.FieldDeclarationStatement;
import smallChanges.statements.MethodDeclarationStatement;
import smallChanges.statements.ParsedSourceFile;
import smallChanges.statements.Statement;

public class SourceFile {

	private HashMap<String, ClassSource> classSource=new HashMap<String,ClassSource>();
	private List<Statement> importList;
	
	public Iterator<String> getClassNameIterator()
	{
		return classSource.keySet().iterator();
	}
	
	public Iterator<ClassSource> getClassIterator()
	{
	    return classSource.values().iterator();
	}
	
//	public ClassSource createClassSource(FlatClassInfo flatClassInfo)
//	{
//	    // create a classDecl by converting the FlatClassInfo and
//	    // its FlatMethodInfo into a MethodDeclarationStatement
//	    // that has a FakeMethodSignature
//	    ClassDeclaration classDecl=new ClassDeclaration();
//	    for (FlatMethodInfo m : flatClassInfo.getFlatMethodInfoList()) {
//	    	classDecl.addMethodDeclarationStatement(m.convertToFakeMethodDeclarationStatement());
//	    }
//	    for( FlatFieldInfo f : flatClassInfo.getFlatFieldInfoList() )
//	    {
//	    	classDecl.addFieldDeclaration(f.convertToFakeFieldDeclarationStatement());
//	    }
//	    StatementList staticInit = new StatementList(null);
//	    for( Statement s : flatClassInfo.getStaticInitializer())
//	    {
//	    	staticInit.addStatement(s);
//	    }
//	    if( flatClassInfo.getStaticInitializer() != null && flatClassInfo.getStaticInitializer().size() > 0)
//	    {
//	    	classDecl.setStaticInitializer(staticInit);
//	    }
//	    return createClassSource(classDecl);
//	    
//	}
	
	public ClassSource createClassSource(ClassDeclaration classDecl)
	{
		ClassSource classSrc = new ClassSource();
		classSrc.setClassDecl(classDecl);
		classSrc.setStaticInitializer(classDecl.getStaticInitializer());
		
		List<MethodDeclarationStatement> methodList = classDecl.getMethodList();
		Iterator<MethodDeclarationStatement> methodIter = methodList.iterator();
		while(methodIter.hasNext())
		{
			MethodDeclarationStatement methodDecl = methodIter.next();
			classSrc.addMethodDecl(methodDecl.getSignature(), methodDecl);
		}
		List<FieldDeclarationStatement> fieldList = classDecl.getFieldList();
		Iterator<FieldDeclarationStatement> fieldIter = fieldList.iterator();
		while(fieldIter.hasNext())
		{
			FieldDeclarationStatement fieldDecl = fieldIter.next();
			classSrc.addFieldDecl(fieldDecl.getName(), fieldDecl);
		}
		List<ClassDeclaration> innerClassList = classDecl.getInnerClasses();
		Iterator<ClassDeclaration> innerClassIter = innerClassList.iterator();
		while(innerClassIter.hasNext())
		{
			ClassDeclaration innerClass = innerClassIter.next();
			String className = innerClass.getName();
			if(className == null)
			{
				
				className = innerClass.getHeader().getImage();
			}
			//System.err.println("FOUND INNER CLASS: "+className);
			classSrc.addInnerClassSource(className,createClassSource(innerClass));
		}
		
		return classSrc;
	}
	
//	public SourceFile(Long fileID, Long transID, Session session)
//	{
//	    // THIS IS WHERE THE SOURCE FILE WILL INTERACT WITH THE DB
//
//	    FlatParsedSourceFile psf=FlatParsedSourceFile.lookupByFileIDTransID(
//                fileID,
//                transID,
//                session, 
//                true);
//	    List<FlatClassInfo> flatClasses=psf.getFlatClassInfoList();
//	    
//	    for (FlatClassInfo c : flatClasses) {
//	        classSource.put(c.getClassname(), createClassSource(c));
//	    }
//	    importList = psf.getImportStatementList();
//	}
	
	public SourceFile(String filename, String srcVersion)
	{
		
		try {
		    ParsedSourceFile parsedSourceFile=
		        ExtractStatements.extractClassDeclaration(srcVersion, filename);
			List<ClassDeclaration> classDeclList = 
			    parsedSourceFile.getTopLevelClassDeclarations();
			// TODO what's up with the includes here, and how to
			// flatten the inner classes?
			Iterator<ClassDeclaration> classDeclIter = classDeclList.iterator();
			while(classDeclIter.hasNext())
			{
				ClassDeclaration classDecl = classDeclIter.next();
				classSource.put(classDecl.getName(),createClassSource(classDecl));				
			}
			importList = parsedSourceFile.getImportStatements().flatten();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public ClassSource getClass(String className)
	{
		ClassSource classSrc;
		
		classSrc = classSource.get(className);
		return classSrc;
	}
	
	public ClassSource getClass(String [] className)
	{
		ClassSource classSrc;
		
		classSrc = classSource.get(className[0]);
		for(int i=1;i<className.length;i++)
		{
			classSrc = classSrc.getInnerClass(className[i]);
		}
		return classSrc;
	}
	
	// TODO don't return MDS
	public MethodDeclarationStatement getMethodDeclarationStatement(String [] className, String sig)
	{
		ClassSource classSrc;
		
		classSrc = getClass(className);
		
		return classSrc.getMethodDeclarationStatement(sig);
	}
	
	public String toString()
	{
		return null;
	}

//	public Iterator<String> getMethodSignatureIterator(String [] className)
//	{
//		ClassSource classSrc;
//		
//		classSrc = getClass(className);
//		
//		return classSrc.getMethodSignatureIterator();
//	}

//	private String readEntireFile(Reader file, ArrayList<String> lines) throws IOException
//	{
//		BufferedReader reader = new BufferedReader(file);
//		String line;
//		String bodyStr = new String();
//
//		lines.add("");// fill line 0
//
//		while ((line = reader.readLine()) != null) {
//			lines.add(line);
//			bodyStr += line +"\n";
//		}
//		return bodyStr;
//	}

	public ArrayList<String> getCleanedOutMethodBody(String [] className, String methodSignature, ArrayList<Statement> statements)
	{
		ClassSource classSrc;
		
		classSrc = getClass(className);
		return classSrc.getCleanedOutMethodBody(methodSignature, statements);
	}
	
	public int getMethodStartLine(String [] className, String methodSignature)
	{
		ClassSource classSrc;
		
		classSrc = getClass(className);

		return classSrc.getMethodDeclarationStatement(methodSignature).getBeginLine();
	}

    public List<Statement> getImportList() {
        return importList;
    }

    public void setImportList(List<Statement> importList) {
        this.importList = importList;
    }

}
